//contains globals and other stuff needed by the engine
//using the loose augmentation module pattern so that each file can
//load asynchously

//global helpers. maybe make them not global?
function memoize(f) {
    return function () {
        var args = Array.prototype.slice.call(arguments);
        f.memo = f.memo || {};
        return (args in f.memo)? f.memo[args] :
            f.memo[args] = f.apply(this, args);
    };
}

function for_each(objs, fn) {
    objs = objs || [];
    fn = fn || function() {};
    var args = Array.prototype.slice.call(arguments, 2);
    for(var i = 0; i < objs.length; i++) {
        fn.apply(objs[i],args);
    }
};


//use '_doll.mod.member = blah' to set up the submodules
//use '_doll.parent = doll.parent || {}' before doing
//'doll.parent.extend = blah'
var doll = (function (_doll) {
    "use strict";
    _doll.version = '1';
    _doll.scene = null; //current scene
    _doll.camera = null; //current camera
    _doll.window_height = 0;
    _doll.window_width = 0;

    _doll.error = function(msg, data) {
        this.msg = msg;
        this.data = data;
    }

    _doll.window_resize = function() {
        _doll.window_width = window.innerWidth;
        _doll.window_height = window.innerHeight;
        _doll.camera.aspect = _doll.window_width / _doll.window_height;
	_doll.camera.updateProjectionMatrix();
	_doll.render.setSize(_doll.window_width, _doll.window_height );
	_doll.render.render();
        if(_doll.stats) {
            _doll.stats.update();
        }
    }

    //call after everything is loaded
    _doll.init = function() {
        console.log('*Doll Initialization*');
        if(window) {
            window.addEventListener('resize', _doll.window_resize, false);
        }
        _doll.text.init();
        _doll.input.init();
        _doll.ui.init();
        _doll.fetch.init();
        _doll.audio.init();
        console.log('*Doll Initialization Complete*');
    }

    _doll.RAD = Math.PI/180;
    _doll.HALFPI = Math.PI/2;
    _doll.YAXIS = new THREE.Vector3(0,1,0);
    _doll.XAXIS = new THREE.Vector3(1,0,0);
    _doll.ZAXIS = new THREE.Vector3(0,0,1);
    _doll.rad = function (d) { return d * _doll.RAD; }


    //debug renderables
    _doll.debug_grid = function() {
        var grid = new THREE.Mesh( new THREE.PlaneGeometry( 1000, 1000, 20, 20), 
                                   new THREE.MeshBasicMaterial( { color: 0x555555, wireframe: true}));
        grid.rotation.x = _doll.HALFPI;
        return grid;
    }

    _doll.fps = function(f) {
        _doll.FPS = 1000.0 / f;
    }

    //helpers maybe put somewhere else?
    _doll.orbit = function(v, t, R, phi, theta) {
        v.copy(t);
        v.x += R * Math.cos(phi) * Math.sin(theta);
        v.z += R * Math.sin(phi) * Math.sin(theta);
        v.y += R * Math.cos(theta);//because y is up
    };

    _doll.orbit_anim = function(orbit, r, p, t, ps, ts, pEnd, tEnd) {
        var position = new THREE.Vector3();
        var phi = p || 0;
        var theta = t || 0;
        var target = orbit || new THREE.Vector3(0,0,0);
        var R = r || 100;
        var pspeed = ps || 0;
        var tspeed = ts || 0;
        var phi_end = pEnd;
        var theta_end = tEnd;
        var done = false;
        var paused = false;
        return {
            play: function() { 
                position.set(0,0,0);
                phi = p || 0;
                theta = t || 0;
                R = r || 100;
                pspeed = ps || 0;
                tspeed = ts || 0;
                phi_end = pEnd;
                theta_end = tEnd;
                paused = false;
                done = false;
            },
            pause: function(p) {
                p = p || !paused;
                paused = p;
            },
            direction: function(d) {
                d = d || -1;
                pspeed = pspeed * d;
                tspeed = tspeed * d;
            },
            step: function() {
                if(done || paused) {
                    return position;
                }
                _doll.orbit(position, target, R, phi, theta);
                phi += pspeed;
                theta += tspeed;

                if(phi_end !== undefined) {
                    done = phi >= phi_end;
                }

                if(theta_end !== undefined) {
                    done = theta >= theta_end;
                }

                return position;
            },
            is_playing: function() {
                return !done && !paused;
            }
        };
    };

    function greet(greeting) {
        return function () {
            console.log(greeting + this.name);
        }
    };

    function age(pre, post) {
        console.log(pre + this.age + post);
    };

    var bob = { name: 'Bob', age: 20 }
    var sue = { name: 'Sue', age: 19 }
    var mary = { name: 'Mary', age: 12 }
    
    var o = [bob, sue, mary];

    for_each(o, greet("hello the names's "));
    for_each(o, age, 'I am ', ' years old');


    _doll.set_options = function(obj, opts) {
        for (o in opts) {
            var val = opts[o];
            if(val === undefined) {
                console.warn('doll.set_options: undefined value ' + val + ' passed as option.');
                continue;
            }
            
            if(o in obj) {
                var curVal = obj[o];
                if(curVal instanceof THREE.Color) {
                    curVal.set(val);
                } else if(curVal instanceof THREE.Vector3 && val instanceof THREE.Vector3) {
                    curVal.copy(val);
                } else {
                    obj[o] = val;
                }
            } else {
                console.warn('doll.set_options: tried to set a value that an object does not have: ' + o);
            }
        }
    };

    return _doll;
}(doll || {}));


